// 根据中间拖拽层表单组件实例 渲染中间可拖拽的<form-item>xx组件</form-item>表单组件 ./components 目录下存放自定义表单组件以及对应得表单组件实例

import { h, computed, ref } from 'vue'
import type { ComputedRef, PropType, Reactive, Ref } from 'vue'
import {
    Form,
    Input,
    Radio,
    FormItem,
    Checkbox,
    RadioGroup,
    CheckboxGroup,
    Switch,
    InputNumber,
    Select,
    RangePicker,
    Rate,
    Cascader
} from '@arco-design/web-vue'
import BaseEntity from './components/BaseEntity'
import { VueDraggable } from 'vue-draggable-plus'
import SpaceLayout from './components/SpaceLayout/index.vue'
import SpaceLayoutEntity from './components/SpaceLayout/entity/SpaceLayoutEntity'
import formContainer from '../form-container.vue'
import useForm from '../../hooks/useForm'
import { entityType } from './components'
import upload from './components/Upload/upload.vue'
import Table from './components/Table/Table.vue'
import Signature from './components/Signature/index.vue'
//去除定义类型中 指定的key
type RestrictKey<T, K extends keyof any> = {
    [P in keyof T]: P extends K ? never : T[P]
}

export type formElementType =
    | 'radio'
    | 'input'
    | 'checkbox'
    | 'slot'
    | 'radioGroup'
    | 'checkboxGroup'
    | 'switch'
    | 'inputNumber'
    | 'select'
    | 'spaceLayout'

//FormPropsType 类型中的属性与官网form 组件的属性保持一致 但不用再声明model属性
export type FormPropsType = {
    draggableFormEntity: BaseEntity
    parentField: Array<string>
    parentEntity: BaseEntity
}

export default {
    components: {
        Form,
        Input,
        Radio,
        FormItem,
        Checkbox
    },
    props: {
        draggableFormEntity: {
            type: Object as PropType<BaseEntity>
        },
        //当存在嵌套表单组件时(例如布局组件中的分栏组件)传递父级组件的field 用于拼接当前组件的field
        parentField: {
            type: Array as PropType<Array<string>>,
            default: () => []
        },
        //当存在嵌套表单组件时(例如布局组件中的分栏组件)传递父级组件的实体
        parentEntity: {
            type: Object as PropType<BaseEntity>
        }
    },
    setup(props: FormPropsType, { expose, emit, slots }: any) {
        let { renderConfig, FormEntitys } = useForm()

        //渲染表单项组件
        const formItemComponent = computed(() => {
            props.draggableFormEntity.config.onClick = (event: Event) => {
              event.stopPropagation() // 阻止事件冒泡
             
            
                emit('select', props.draggableFormEntity)
            }

            //解决因组件嵌套出现父级组件 子组件需要从父组件获取值
            handleMultilevelNesting(props.draggableFormEntity,props.parentEntity)

            //处理表单元素modelValue双向绑定数据事件 当存在父级组件时 子组件监听到数据更新后需要赋值给父组件，因为若存在父级组件那么最终子组件获取值始终从父组件获取
            handleFormItemElModelValue(props.draggableFormEntity,props.parentEntity)


            let slotComp

            switch (props.draggableFormEntity.type) {
                case 'slot':
                    slotComp = h(
                        'div',
                        {
                            style: {
                                width: '100%',
                                height: '100%'
                            }
                        },
                        slots[props.draggableFormEntity.config.field](
                            props.draggableFormEntity.slotFormConfig
                        )
                    )
                    break
                case 'input':
                    slotComp = h(Input, props.draggableFormEntity.slotFormConfig)
                    break
                case 'radio':
                    slotComp = h(Radio, props.draggableFormEntity.slotFormConfig)
                    break
                case 'radioGroup':
                    slotComp = h(RadioGroup, props.draggableFormEntity.slotFormConfig)
                    break
                case 'checkbox':
                    slotComp = h(Checkbox, props.draggableFormEntity.slotFormConfig)
                    break
                case 'checkboxGroup':
                    slotComp = h(CheckboxGroup, props.draggableFormEntity.slotFormConfig)
                    break
                case 'switch':
                    slotComp = h(Switch, props.draggableFormEntity.slotFormConfig)
                    break
                case 'inputNumber':
                    slotComp = h(InputNumber, props.draggableFormEntity.slotFormConfig)
                    break
                case 'select':
                    slotComp = h(Select, props.draggableFormEntity.slotFormConfig)
                    break
                case 'space-layout':
                    slotComp = h(SpaceLayout, {
                        entity: props.draggableFormEntity as SpaceLayoutEntity,
                        parentField: props.parentField || []
                    })
                    break
                case 'dateRange':
                    slotComp = h(RangePicker, props.draggableFormEntity.slotFormConfig)
                    break
                case 'upload':
                    slotComp = h(upload, props.draggableFormEntity.slotFormConfig)
                    break
                case 'table':
                    slotComp = h(Table, props.draggableFormEntity.slotFormConfig)
                    break
                case 'rate':
                    slotComp = h(Rate, props.draggableFormEntity.slotFormConfig)
                    break
                case 'signature':
                    slotComp = h(Signature, props.draggableFormEntity.slotFormConfig)
                    break
                case 'cascader':
                    slotComp = h(Cascader, props.draggableFormEntity.slotFormConfig)
                    break
            }
            const comp = h(
                FormItem,
                {
                    ...props.draggableFormEntity.config,
                    field: props.parentField
                        .concat(props.draggableFormEntity.config.field)
                        .join('.')
                },
                slotComp
            )
            return comp
        })

        return () =>
            [props.draggableFormEntity.config.hidden?null:h(
                formContainer,
                {
                    draggableFormEntity: props.draggableFormEntity,
                    parentField: props.parentField,
                    parentEntity: props.parentEntity
                },
                formItemComponent.value
            )]
    }
}


//处理表单元素双向绑定得事件 所有得表单元素 第三方表单元素或是自定义表单元素都需要通过modelValue与表单进行双向绑定。因为表单元素得值始终与表单一一对应
function handleFormItemElModelValue(draggableFormEntity: Reactive<BaseEntity>,parentEntity: Reactive<BaseEntity>){
  draggableFormEntity.slotFormConfig['onUpdate:modelValue'] = (e: any) => {

    // emit('onUpdate:modelValue', e)`
    if (parentEntity) {//如果存在父组件 则子组件得监听到数据变化时赋值给父组件。因为给父组件中赋值是表单校验层级得需要
       parentEntity.slotFormConfig.modelValue[
          draggableFormEntity.config.field
        ] = e
    }  
    //始终保证每次元素值变化都同步到自己实例上，因为实例组件得值永远都需要保证唯一获取路径就是从自己实例上获取
    draggableFormEntity.slotFormConfig.modelValue = e
}

}

//根据父级层级 解决因组件嵌套出现多层表单 子表单项赋值父级表单出现得问题 例如多个分栏组件嵌套等等
function handleMultilevelNesting( draggableFormEntity: Reactive<BaseEntity>,parentEntity: Reactive<BaseEntity>){
    //解决因组件嵌套导致得表单校验层级出现的问题 例如多个分栏组件嵌套等等
    if (parentEntity) { //如果存在父级组件 则父组件中得值永远从子组件中获取 但是子组件可能同时也为嵌套组件例如layout嵌套layout 此时layout
      parentEntity.slotFormConfig.modelValue[
        draggableFormEntity.config.field
      ] = draggableFormEntity.slotFormConfig.modelValue

  }
}
